package fr.enssat.embeddedsoft.projet.fgourvel.spotrin.bluetooth;

import java.io.Serializable;
import java.util.UUID;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

/**
 * Gestion de la communication bluetooth via les treads :
 * 	- d'écoute : AcceptThread
 *  - de connection : ConnectThread
 *  - de transfert : ConnectedThread
 */
public class BluetoothService implements Serializable{
    
    private static final long serialVersionUID = 1L;
    private static String TAG = "BluetoothService";

        // UUID Unique pour l'application
    public static final UUID APP_UUID =
        UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
    
    // les états de la connexion
    	// Etat initial, on ne fait rien 
    public static final int ETAT_NUL            = 0; 
    	// écoute des connexions entrantes
    public static final int ETAT_ECOUTE         = 1; 
    	// connexion avec le périphérique distant
    public static final int ETAT_CONNEXION      = 2; 
    	// communication avec le périphérique distant
    public static final int ETAT_CONNECTE       = 3; 
    private int etat;
    
      
    private static BluetoothService instance;
    private Context context;
    private BluetoothAdapter adapter;
    private Handler handler;
    public String message;
    
    //déclarations des trois types de Thread
    private AcceptThread ecouteThread;
    private ConnectThread connexionThread;
    private ConnectedThread gestionConnexionThread;
    
    
    /*********************************************************
    /*               CONSTRUCTEUR
     *********************************************************/   

    
    private BluetoothService(Context context){
        this.context = context.getApplicationContext();
        this.adapter = BluetoothAdapter.getDefaultAdapter();
        this.etat = ETAT_NUL;
    }
    
    

    /*********************************************************
    /*                 GETTERS & SETTERS 
     *********************************************************/
    
    public static BluetoothService getInstance(Context context) {
        if (null == instance) { 
            instance = new BluetoothService(context);
        }
        return instance;
    }
    
    public Context getContext() {
            return context;
    }

    public BluetoothAdapter getAdapter() {
            return adapter;
    }

    public void setHandler(Handler handler) {
            this.handler = handler;
    }

    public Handler getHandler() {
            return handler;
    }

    public synchronized int getState() {
            return etat;
    }

    /*********************************************************
    /*MISE EN PLACE DE L'ECOUTE DES DE DEMANDES DE CONNEXIONS 
     *********************************************************/
    
    public synchronized void start() {
        // On arrête le thread qui tente d'établir une connexion
        if (connexionThread != null) {
                connexionThread.cancel();
                connexionThread = null;
        }
        // On arrête le thread qui gère les connexions
        if(gestionConnexionThread != null) {
                gestionConnexionThread.cancel();
                gestionConnexionThread = null;
        }
        // On lance le thread d'écoute sur la socket
        if(ecouteThread == null){
                ecouteThread = new AcceptThread(this);
                ecouteThread.start();
        }
        // On passe à l'état d'écoute
        etat = ETAT_ECOUTE;
    }
    
    
    
		    /*******************************
		    /*MISE EN PLACE DES CONNEXIONS 
		     *******************************/    
    
    public synchronized void connect(BluetoothDevice device){
        Log.d(TAG, "Se connecte à : " + device);
        // Si on a commencé un connexion avec un périphérique distant
        if(etat == ETAT_CONNEXION){
                // On arrête le thread qui tente d'établir une connexion
                if(connexionThread != null){
                        connexionThread.cancel();
                        connexionThread = null;
                }
        }
        // On arrête le thread qui gère les connexions
        if(gestionConnexionThread != null) {
                gestionConnexionThread.cancel();
                gestionConnexionThread = null;
        }
        // On lance le thread qui se connecte au périphérique donné
        connexionThread = new ConnectThread(this, device);
        connexionThread.start();
        // On passe à l'état de connexion avec le périphérique
        etat = ETAT_CONNEXION;
    }
    
		    /*******************************
		    /*MISE EN PLACE DU TRAFFIQUE     
		    *******************************/
    
    public synchronized void connected(BluetoothSocket socket, BluetoothDevice device){
        // On arrête le thread qui tente d'établir une connexion
        if (connexionThread != null) {
                connexionThread.cancel();
                connexionThread = null;
        }
        // On arrête le thread qui gère les connexions
        if(gestionConnexionThread != null) {
                gestionConnexionThread.cancel();
                gestionConnexionThread = null;
        }
        // On arrête le thread d'écoute sur la socket car on se connecte à un seul périphérique
        if(ecouteThread == null){
                ecouteThread.cancel();
                ecouteThread = null;
        }
        // On lance le thread qui gère les connexions
        gestionConnexionThread = new ConnectedThread(this, socket);
        gestionConnexionThread.start();
        
       
        Message msg = handler.obtainMessage();
        Bundle bundle = new Bundle();
        bundle.putString(message, "test");
        msg.setData(bundle);
        handler.sendMessage(msg);
        
        // On passe à l'état connecté avec le périphérique distant
        etat = ETAT_CONNECTE;
    }
    
    
		    /*******************************
		    /*   ARRET DE LA COMMUNICATION 
		     * *****************************/
    
    public synchronized void stop(){
        // On arrête le thread qui tente d'établir une connexion
        if (connexionThread != null) {
                connexionThread.cancel();
                connexionThread = null;
        }
        // On arrête le thread qui gère les connexions
        if(gestionConnexionThread != null) {
                gestionConnexionThread.cancel();
                gestionConnexionThread = null;
        }
        // On arrête le thread d'écoute sur la socket
        if(ecouteThread == null){
                ecouteThread.cancel();
                ecouteThread = null;
        }
        // On passe à l'état où on ne fait rien
        etat = ETAT_NUL;
    }
    
    /*******************************
    /*  ECRITURE DISTANTE
     * *****************************/
    
    public void write(byte[] out){
        ConnectedThread asyncThread = null;
       
        synchronized (this) {
                if(etat != ETAT_CONNECTE){
                        asyncThread = gestionConnexionThread;
                }
        }
        
        asyncThread.write(out);
    }

    
    /*******************************
    /*  GESTION DES ERREURS 
     * *****************************/
    
    public void connectionFailed(){
       
        Message msg = handler.obtainMessage();
        Bundle bundle = new Bundle();
        bundle.putString(message, "erreur_connection_failed");
        msg.setData(bundle);
        handler.sendMessage(msg);    
        // On recommence à écouter
        start();
    }
    
    public void connectionLost(){
       
        Message msg = handler.obtainMessage();
        Bundle bundle = new Bundle();
        bundle.putString(message, "erreur_connection_lost");
        msg.setData(bundle);
        handler.sendMessage(msg);    
        // On recommence à écouter
        start();
    }
}
